package fun.tan90.device.event;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ClassLoaderUtil;
import fun.tan90.device.event.model.ClientInitFailure;
import fun.tan90.device.event.model.ClientInitSuccess;
import fun.tan90.device.event.model.ServerInitFailure;
import fun.tan90.device.event.model.ServerInitSuccess;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.noear.dami.Dami;
import org.noear.snack.ONode;
import org.noear.solon.annotation.Inject;
import org.noear.solon.core.AppContext;
import org.noear.solon.core.bean.LifecycleBean;

import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

@Slf4j
@Getter
@Setter
public abstract class BaseFactory implements LifecycleBean {

    @Inject("${plugin-name}")
    String pluginName;

    @Inject
    AppContext appContext;

    private ONode connectNode;

    private Class<?> clz;

    public abstract Class<?> init(ONode connectNode, BiConsumer<String, Object> biConsumer);

    public abstract String topicName();

    @Override
    public void start() {
        String topicName = topicName();
        this.connectNode = Dami.<String, ONode>bus().sendAndRequest(topicName, pluginName);
        log.info("{}: {}", topicName, connectNode.toJson());
        this.clz = init(connectNode, (key, obj) -> {
            if (ClassLoaderUtil.isPresent(key)) {
                appContext.wrapAndPut(ClassLoaderUtil.loadClass(key), obj);
            } else {
                appContext.wrapAndPut(key, obj);
            }
        });
    }

    public void post(Map<String, ONode> ruleMap, Map<String, ?> clientMap) {
    }

    public abstract ONode ruleNode();

    @Override
    public void postStart() {
        ONode ruleNode = ruleNode();
        if (Objects.isNull(ruleNode)) {
            return;
        }
        Map<String, ONode> ruleMap = ruleNode.ary().stream().collect(Collectors.toMap(node -> node.get("name").getString(), node -> node));
        log.info("RuleMap: {}", ruleMap);
        Map<String, ?> beansMapOfType = appContext.getBeansMapOfType(clz);
        Set<String> ruleMapSet = ruleMap.keySet();
        Set<String> beanMapSet = beansMapOfType.keySet();
        String[] array = ruleMapSet.stream().filter(beanMapSet::contains).distinct().toArray(String[]::new);
        ruleMap = MapUtil.filter(ruleMap, array);
        beansMapOfType = MapUtil.filter(beansMapOfType, array);
        post(ruleMap, beansMapOfType);
    }

    public void clientInitSuccess(String name) {
        Dami.<ClientInitSuccess, Void>bus().send(EventTopic.CLIENT_INIT_SUCCESS_TOPIC, ClientInitSuccess.builder().pluginName(pluginName).name(name).build());
    }

    public void clientInitFailure(String name, Throwable throwable) {
        Dami.<ClientInitFailure, Void>bus().send(EventTopic.CLIENT_INIT_FAILURE_TOPIC, ClientInitFailure.builder().pluginName(pluginName).name(name).throwable(throwable).build());
    }

    public void serverInitSuccess() {
        Dami.<ServerInitSuccess, Void>bus().send(EventTopic.SERVER_INIT_SUCCESS_TOPIC, ServerInitSuccess.builder().pluginName(getPluginName()).build());
    }

    public void serverInitFailure(Throwable throwable) {
        Dami.<ServerInitFailure, Void>bus().send(EventTopic.SERVER_INIT_FAILURE_TOPIC, ServerInitFailure.builder().pluginName(getPluginName()).throwable(throwable).build());
    }
}
